package com.yfp.group.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayPartnerOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.redis.utils.RedisNumber;
import com.yfp.common.security.utils.SecurityUtils;
import com.yfp.group.domain.Enterprise;
import com.yfp.group.domain.EnterpriseCapital;
import com.yfp.group.domain.dto.CapitalSettleDTO;
import com.yfp.group.domain.dto.CreateWxPayOrderDTO;
import com.yfp.group.domain.dto.EnterpriseCapitalDTO;
import com.yfp.group.domain.dto.EnterpriseCapitalQueryDTO;
import com.yfp.group.domain.vo.EnterpriseCapitalOrderVO;
import com.yfp.group.domain.vo.EnterpriseCapitalVO;
import com.yfp.group.enums.*;
import com.yfp.group.mapper.EnterpriseCapitalMapper;
import com.yfp.group.service.EnterpriseCapitalService;
import com.yfp.group.service.EnterpriseService;
import com.yfp.group.service.PayService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author admin
 * @description 针对表【enterprise_user_capital(企业资金明细)】的数据库操作Service实现
 * @createDate 2024-08-10 09:45:44
 */
@Slf4j
@Service
public class EnterpriseCapitalServiceImpl extends ServiceImpl<EnterpriseCapitalMapper, EnterpriseCapital> implements EnterpriseCapitalService {

    @Resource
    private EnterpriseCapitalMapper enterpriseCapitalMapper;
    @Resource
    private EnterpriseService enterpriseService;
    @Resource
    private PayService payService;
    @Resource
    private RedisNumber redisNumber;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 企业资金明细
     *
     * @param enterpriseCapitalDTO
     * @return
     */
    @Override
    public List<EnterpriseCapitalVO> enterpriseCapital(EnterpriseCapitalDTO enterpriseCapitalDTO) {
        List<EnterpriseCapitalVO> list = enterpriseCapitalMapper.enterpriseCapital(enterpriseCapitalDTO);
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public HashMap<String, Object> createRechargeOrder(CapitalSettleDTO capitalSettleDTO) {
        Long userId = SecurityUtils.getUserId();
        RLock lock = redissonClient.getLock(CacheConstants.ENTERPRISE_CAPITAL_ORDER_LOCK_KEY + String.format("%s_%d", userId, capitalSettleDTO.getPayType()));
        Assert.isTrue(lock.tryLock(), "订单创建中");

        try {
            Enterprise enterprise = enterpriseService.findByUserId(userId);

            // 创建订单
            EnterpriseCapital enterpriseCapital = new EnterpriseCapital();
            enterpriseCapital.setEnterpriseId(enterprise.getId());
            enterpriseCapital.setTransactionId(redisNumber.getCRG());
            enterpriseCapital.setType(1);
            enterpriseCapital.setMoney(capitalSettleDTO.getAmount());
            enterpriseCapital.setCreateTime(DateUtil.date());
            enterpriseCapital.setStatus(EnterpriseCapitalPayStatusEnums.WAIT_PAY.getCode());
            enterpriseCapitalMapper.insert(enterpriseCapital);

            Object orderPaymentWx = this.orderPaymentWx(capitalSettleDTO, enterpriseCapital.getTransactionId(), enterprise);
            // 返回订单信息
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("orderSn", enterpriseCapital.getTransactionId());
            hashMap.put("paymentInfo", orderPaymentWx);
            return hashMap;
        } finally {
            long id = Thread.currentThread().getId();
            CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS).execute(() -> {
                lock.unlockAsync(id);
            });
        }
    }

    @Override
    public void createConsumeOrder(EnterpriseCapital enterpriseCapital) {
        // 创建订单
        enterpriseCapital.setCreateTime(DateUtil.date());
        enterpriseCapital.setStatus(EnterpriseCapitalPayStatusEnums.WAIT_PAY.getCode());
        enterpriseCapitalMapper.insert(enterpriseCapital);
    }

    @Override
    public Object orderPaymentWx(CapitalSettleDTO capitalSettleDTO, String orderSn, Enterprise enterprise) {
        // 获取微信支付商户号
        String subMchId = payService.getSubMchIdByMachineId(enterprise.getMachineId());
        // 创建微信支付订单
        CreateWxPayOrderDTO createWxPayOrderDTO = new CreateWxPayOrderDTO();
        createWxPayOrderDTO.setDescription("麦德范-账户充值");
        createWxPayOrderDTO.setOutTradeNo(orderSn);
        createWxPayOrderDTO.setTimeExpire(DateUtil.date().offset(DateField.MINUTE, 30));
        createWxPayOrderDTO.setTotalPrice(capitalSettleDTO.getAmount());
        createWxPayOrderDTO.setSubMchId(subMchId);
        createWxPayOrderDTO.setAttach(WxPayAttach.ACCOUNT_RECHARGE.getCode());

        if (PayTypeEnums.WECHAT.getCode().equals(capitalSettleDTO.getPayType())) {
            createWxPayOrderDTO.setOpenid(capitalSettleDTO.getOpenId());
            // 调用微信支付
            WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPayOrder = payService.createWxJsapiPayOrder(createWxPayOrderDTO);
            String wxPrepayId = StrUtil.replace(wxJsapiPayOrder.getPackageValue(), "prepay_id=", "");
            // 设置交易会话ID
            EnterpriseCapital enterpriseCapital = new EnterpriseCapital();
            enterpriseCapital.setTransactionId(orderSn);
            enterpriseCapital.setPrepayId(wxPrepayId);
            this.updateByTransactionId(enterpriseCapital);
            return wxJsapiPayOrder;
        } else if (PayTypeEnums.WECHAT_QRCODE.getCode().equals(capitalSettleDTO.getPayType())) {
            // 调用微信二维码支付
            String codeUrl = payService.createWxNativePayOrder(createWxPayOrderDTO);
            return codeUrl;
        } else {
            throw new ServiceException("支付类型错误");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void parseOrderNotifyResult(WxPayPartnerNotifyV3Result.DecryptNotifyResult result) {
        EnterpriseCapital enterpriseCapital = enterpriseCapitalMapper.selectByTransactionId(result.getOutTradeNo());
        if (EnterpriseCapitalPayStatusEnums.PAID.getCode().equals(enterpriseCapital.getStatus())) {
            return;
        }
        // 增加余额
        this.addBalance(enterpriseCapital, result);
    }

    @Override
    public EnterpriseCapital getOrderStatus(String orderSn) {
        EnterpriseCapital enterpriseCapital = enterpriseCapitalMapper.selectByTransactionId(orderSn);
        if (EnterpriseCapitalPayStatusEnums.PAID.getCode().equals(enterpriseCapital.getStatus())) {
            // 订单已支付
            return enterpriseCapital;
        }
        // 如果订单是微信支付，或微信二维码支付
        if (PayTypeEnums.WECHAT.getCode().equals(enterpriseCapital.getType())) {
            // 获取微信支付商户号
            Enterprise enterprise = enterpriseService.selectById(enterpriseCapital.getEnterpriseId());
            String subMchId = payService.getSubMchIdByMachineId(enterprise.getMachineId());
            // 调用微信支付查询订单状态
            WxPayPartnerOrderQueryV3Result orderStatus = payService.getOrderStatus(orderSn, subMchId);
            if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(orderStatus.getTradeState())) {
                // 更新订单状态
                WxPayPartnerNotifyV3Result.DecryptNotifyResult decryptNotifyResult = BeanUtil.toBean(orderStatus, WxPayPartnerNotifyV3Result.DecryptNotifyResult.class);
                this.parseOrderNotifyResult(decryptNotifyResult);
                enterpriseCapital.setStatus(EnterpriseOrderPayStatusEnums.PAID.getCode());
                return enterpriseCapital;
            }
            return enterpriseCapital;
        }
        return enterpriseCapital;
    }

    @Override
    public List<EnterpriseCapitalOrderVO> orderList(EnterpriseCapitalQueryDTO enterpriseCapitalQueryDTO) {
        Long userId = SecurityUtils.getUserId();
        Enterprise enterprise = enterpriseService.findByUserId(userId);

        // 时间范围
        if (Objects.nonNull(enterpriseCapitalQueryDTO.getStartTime()) && Objects.nonNull(enterpriseCapitalQueryDTO.getEndTime())) {
            DateTime beginOfDay = DateUtil.beginOfDay(enterpriseCapitalQueryDTO.getStartTime());
            DateTime endOfDay = DateUtil.endOfDay(enterpriseCapitalQueryDTO.getEndTime());
            enterpriseCapitalQueryDTO.setStartTime(beginOfDay);
            enterpriseCapitalQueryDTO.setEndTime(endOfDay);
        } else {
            enterpriseCapitalQueryDTO.setStartTime(null);
            enterpriseCapitalQueryDTO.setEndTime(null);
        }
        // 交易类型[1:支出、2:充值]
        HashMap<Integer, List<Integer>> tradeTypeMap = new HashMap<>();
        tradeTypeMap.put(1, Arrays.asList(6));
        tradeTypeMap.put(2, Arrays.asList(1, 2));
        List<Integer> tradeTypeList = tradeTypeMap.getOrDefault(enterpriseCapitalQueryDTO.getTradeType(), Arrays.asList(1, 2, 6));

        // 查询订单列表
        List<EnterpriseCapital> enterpriseCapitals = enterpriseCapitalMapper.selectOrderH5List(enterpriseCapitalQueryDTO, tradeTypeList, enterprise.getId());

        // 封装订单列表
        List<EnterpriseCapitalOrderVO> enterpriseCapitalOrderVOS = BeanUtil.copyToList(enterpriseCapitals, EnterpriseCapitalOrderVO.class).stream().peek((item) -> {
            EnterpriseCapitalTypeEnums code = EnterpriseCapitalTypeEnums.getByCode(item.getType());
            item.setOrderName(code.getDesc());
            // 交易金额 根据交易类型 转变为正负数
            if (EnterpriseCapitalTypeEnums.CONSUME_WECHAT.getCode().equals(item.getType()) || EnterpriseCapitalTypeEnums.CONSUME_WECHAT_QR.getCode().equals(item.getType()) || EnterpriseCapitalTypeEnums.CONSUME_ALIPAY.getCode().equals(item.getType()) || EnterpriseCapitalTypeEnums.CONSUME_BALANCE.getCode().equals(item.getType())) {
                item.setMoney(item.getMoney().negate());
            }
        }).sorted(Comparator.comparing(EnterpriseCapitalOrderVO::getCreateTime).reversed()).collect(Collectors.toList());
        return enterpriseCapitalOrderVOS;
    }

    @Override
    public void updateByTransactionId(EnterpriseCapital enterpriseCapital) {
        enterpriseCapitalMapper.updateByTransactionId(enterpriseCapital);
    }

    @Override
    public void addBalance(EnterpriseCapital enterpriseCapital, WxPayPartnerNotifyV3Result.DecryptNotifyResult result) {
        // 更新企业账户 余额
        Enterprise enterprise = enterpriseService.selectById(enterpriseCapital.getEnterpriseId());
        BigDecimal balance = enterprise.getBalance().add(enterpriseCapital.getMoney());
        enterprise.setBalance(balance);
        enterpriseService.updateById(enterprise);
        // 更新订单状态,余额
        enterpriseCapital.setPayTime(DateUtil.parse(result.getSuccessTime()));
        enterpriseCapital.setStatus(EnterpriseCapitalPayStatusEnums.PAID.getCode());
        enterpriseCapital.setBalance(balance);
        enterpriseCapitalMapper.updateById(enterpriseCapital);
    }

    @Override
    public void subBalance(CreateWxPayOrderDTO createWxPayOrderDTO) {
        Long userId = SecurityUtils.getUserId();
        // 更新企业账户 余额
        Enterprise enterprise = enterpriseService.findByUserId(userId);
        BigDecimal balance = enterprise.getBalance().subtract(createWxPayOrderDTO.getTotalPrice());
        enterprise.setBalance(balance);
        enterpriseService.updateById(enterprise);
        // 更新订单余额
        EnterpriseCapital enterpriseCapital = new EnterpriseCapital();
        enterpriseCapital.setTransactionId(createWxPayOrderDTO.getOutTradeNo());
        enterpriseCapital.setBalance(balance);
        enterpriseCapitalMapper.updateByTransactionId(enterpriseCapital);
    }
}




